

#define GLFW_INCLUDE_VULKAN

#include "ce_vulkan_manager.h"
#include "ce_vulkan_context.h"

// 校验层
const bool enableValidationLayers = true;
std::vector<const char *> ValidationLayers = {"VK_LAYER_KHRONOS_validation"};
std::vector<const char *> DeviceExtensions = {VK_KHR_SWAPCHAIN_EXTENSION_NAME};

CE_VulkanManager *CE_VulkanManager::getInstance()
{
    static CE_VulkanManager _instance;
    return &_instance;
}
CE_VulkanManager::CE_VulkanManager(/* args */)
{
}
void CE_VulkanManager::init(GLFWwindow *window)
{
    std::cout << "VULKAN INIT: " << std::endl;
    this->_window = window;

    this->_createInstance();
    this->_createSurface();
    this->_initPhysicalDevice();
    this->_createLogicalDevice();

    this->_context = new CE_VulkanContext(this->_window, this->_physicalDevice, this->_vkdevice, this->_surface);
}
void CE_VulkanManager::_createInstance()
{
    if (!_checkValidationLayerSupport())
    {
        throw std::runtime_error("Validation layers requested, but not available!");
    }
    // 程序信息
    VkApplicationInfo appInfo = {};
    appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
    appInfo.pApplicationName = "Hello Triangle";
    appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
    appInfo.pEngineName = "No Engine";
    appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
    appInfo.apiVersion = VK_API_VERSION_1_0;
    std::cout << "VkApplicationInfo..." << std::endl;

    // 实例信息
    VkInstanceCreateInfo createInfo = {};
    createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
    createInfo.flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR; // 关键！

    createInfo.pApplicationInfo = &appInfo;
    std::cout << "VkInstanceCreateInfo..." << std::endl;

    auto extensions = this->_getRequiredExtensions();
    createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
    createInfo.ppEnabledExtensionNames = extensions.data();
    createInfo.enabledLayerCount = 0;
    createInfo.pNext = nullptr;
    if (enableValidationLayers)
    {
        VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo;
        createInfo.enabledLayerCount = static_cast<uint32_t>(ValidationLayers.size());
        createInfo.ppEnabledLayerNames = ValidationLayers.data();

        this->populateDebugMessengerCreateInfo(debugCreateInfo);
        createInfo.pNext = (VkDebugUtilsMessengerCreateInfoEXT *)&debugCreateInfo;
    }

    if (vkCreateInstance(&createInfo, nullptr, &this->_vkinstance) != VK_SUCCESS)
    {
        throw std::runtime_error("Failed to create instance!");
    }
    std::cout << "VkInstance  Success..." << std::endl;
}
bool CE_VulkanManager::_checkValidationLayerSupport()
{
    uint32_t layerCount;
    vkEnumerateInstanceLayerProperties(&layerCount, nullptr);

    std::vector<VkLayerProperties> availableLayers(layerCount);
    vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data());
    for (const char *layerName : ValidationLayers)
    {
        bool layerFound = false;

        for (const auto &layerProperties : availableLayers)
        {
            if (strcmp(layerName, layerProperties.layerName) == 0)
            {
                layerFound = true;
                break;
            }
        }

        if (!layerFound)
        {
            return false;
        }
    }
    return true;
}
// 消息回调 创建一个getRequiredExtensions函数，这个函数根据是否启用校验层，返回所需的扩展列表
std::vector<const char *> CE_VulkanManager::_getRequiredExtensions()
{
    uint32_t glfwExtensionCount = 0;
    const char **glfwExtensions;
    glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);

    std::vector<const char *> extensions(glfwExtensions,
                                         glfwExtensions + glfwExtensionCount);

    extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); // 添加调试扩展
    extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);
    extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
    extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME);

    return extensions;
}
void CE_VulkanManager::populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT &createInfo)
{
    createInfo = {};
    createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
    createInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
    createInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
    createInfo.pfnUserCallback = this->debugCallback;
}

void CE_VulkanManager::_createSurface()
{
    if (glfwCreateWindowSurface(this->_vkinstance, this->_window, nullptr, &this->_surface) != VK_SUCCESS)
    {
        throw std::runtime_error("Failed to create window surface!");
    }
    std::cout << "VkSurfaceKHR  Success..." << std::endl;
}

void CE_VulkanManager::_initPhysicalDevice()
{
    this->_physicalDevice = VK_NULL_HANDLE;
    // 查询显卡的数量：
    uint32_t deviceCount = 0;
    vkEnumeratePhysicalDevices(this->_vkinstance, &deviceCount, nullptr);
    if (deviceCount == 0)
    {
        throw std::runtime_error("Failed to find GPUs with Vulkan support!");
    }
    // 分配数组来存储VkPhysicalDevice对象
    std::cout << "Physical Device Count: " << deviceCount << std::endl;
    std::vector<VkPhysicalDevice> devices(deviceCount);
    vkEnumeratePhysicalDevices(this->_vkinstance, &deviceCount, devices.data());
    // 检查所有可用设备，并选择第一个满足需求的设备：
    for (const auto &device : devices)
    {
        if (this->_isDeviceSuitable(device))
        {
            this->_physicalDevice = device;
            break;
        }
    }
    std::cout << "Physical Device: " << this->_physicalDevice << std::endl;
    // std::cout << "Physical Device: " << devices << std::endl;
    if (this->_physicalDevice == VK_NULL_HANDLE)
    {
        throw std::runtime_error("Failed to find a suitable a GPU.");
    }
}

// 为了选择合适的设备，我们需要或许详细的设备信息。包括但不限于：名称、类型和支持Vulkan的版本。
bool CE_VulkanManager::_isDeviceSuitable(VkPhysicalDevice device)
{
    // VkPhysicalDeviceProperties deviceProperties;
    // vkGetPhysicalDeviceProperties(device, &deviceProperties);

    // // 纹理压缩、64为浮点、多窗口渲染是否支持，通过下面函数查询
    // VkPhysicalDeviceFeatures deviceFeatures;
    // vkGetPhysicalDeviceFeatures(device, &deviceFeatures);

    // // 显卡支持集合着色器的判断条件
    // bool isSupportSetShader = (deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) && deviceFeatures.geometryShader;
    // std::cout << "isSupportSetShader: " << isSupportSetShader << std::endl;
    // // return isSupportSetShader;

    QueueFamilyIndices indices = this->findQueueFamilies(device);
    // 由于我们只需要显示三角形，所以不需要额外特性，直接返回true即可，以上代码作为测试
    bool extensionsSupported = this->_checkDeviceExtensionSupport(device);

    bool swapChainAdequate = false;
    if (extensionsSupported)
    {
        SwapChainSupportDetails swapChainSupport = this->querySwapChainSupport(device);
        swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty();
    }

    return indices.isComplete() && extensionsSupported && swapChainAdequate;
}
QueueFamilyIndices CE_VulkanManager::findQueueFamilies(VkPhysicalDevice device)
{
    QueueFamilyIndices indices;

    uint32_t queueFamilyCount = 0;
    vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr);
    std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
    vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data());

    int index = 0;
    for (const auto &queueFamily : queueFamilies)
    {
        if (queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT)
        { // 目前，我们只要寻找图形队列即可
            indices.graphicsFamily = index;
        }

        VkBool32 presentSupport = false;
        vkGetPhysicalDeviceSurfaceSupportKHR(device, index, this->_surface, &presentSupport);
        if (presentSupport)
        {
            indices.presentFamily = index;
        }

        if (indices.isComplete())
        {
            break;
        }
        index++;
    }

    return indices;
}
bool CE_VulkanManager::_checkDeviceExtensionSupport(VkPhysicalDevice device)
{
    uint32_t extensionCount;
    vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr);

    std::vector<VkExtensionProperties> availableExtensions(extensionCount);
    vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data());

    std::set<std::string> requiredExtensions(DeviceExtensions.begin(), DeviceExtensions.end());

    for (auto extension : DeviceExtensions)
    {
        std::cout << "extension: " << extension << std::endl;
    }

    for (const auto &extension : availableExtensions)
    {
        requiredExtensions.erase(extension.extensionName);
    }

    return requiredExtensions.empty();
}
SwapChainSupportDetails CE_VulkanManager::querySwapChainSupport(VkPhysicalDevice device)
{
    SwapChainSupportDetails details;

    // 与交换链相关的函数都需要device和surface这两个参数
    // 查询基础表面特性
    vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, this->_surface, &details.capabilities);

    // 查询表面支持格式
    uint32_t formatCount;
    vkGetPhysicalDeviceSurfaceFormatsKHR(device, this->_surface, &formatCount, nullptr);
    if (formatCount != 0)
    {
        details.formats.resize(formatCount);
        vkGetPhysicalDeviceSurfaceFormatsKHR(device, this->_surface, &formatCount, details.formats.data());
    }

    // 查询表面支持呈现模式
    uint32_t presentModeCount;
    vkGetPhysicalDeviceSurfacePresentModesKHR(device, this->_surface, &presentModeCount, nullptr);

    if (presentModeCount != 0)
    {
        details.presentModes.resize(presentModeCount);
        vkGetPhysicalDeviceSurfacePresentModesKHR(device, this->_surface, &presentModeCount, details.presentModes.data());
    }

    return details;
}
// 选择物理设备之后，并不能直接与之交互，还需要一个逻辑设备作为接口交互
void CE_VulkanManager::_createLogicalDevice()
{
    QueueFamilyIndices indices = this->findQueueFamilies(this->_physicalDevice);
    std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
    std::set<uint32_t> uniqueQueueFamilies = {indices.graphicsFamily, indices.presentFamily};
    float queuePriority = 1.0f;
    std::cout << "uniqueQueueFamilies: " << uniqueQueueFamilies.size() << std::endl;
    for (uint32_t queueFamily : uniqueQueueFamilies)
    {
        VkDeviceQueueCreateInfo queueCreateInfo{};
        queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
        queueCreateInfo.queueFamilyIndex = queueFamily;
        queueCreateInfo.queueCount = 1;
        queueCreateInfo.pQueuePriorities = &queuePriority;
        queueCreateInfos.push_back(queueCreateInfo);
    }

    // 指定应用程序使用的设备特性
    VkPhysicalDeviceFeatures deviceFeatures = {};
    deviceFeatures.geometryShader = VK_FALSE;
    deviceFeatures.tessellationShader = VK_FALSE; // 例如禁用曲面细分

    // 创建逻辑设备
    VkDeviceCreateInfo createInfo = {};
    createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
    createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size());
    createInfo.pQueueCreateInfos = queueCreateInfos.data();
    createInfo.pEnabledFeatures = &deviceFeatures;

    // For macos
    std::vector<const char*> localDeviceExtensions = DeviceExtensions;
    uint32_t extensionCount;
    vkEnumerateDeviceExtensionProperties(this->_physicalDevice, nullptr, &extensionCount, nullptr);
    std::vector<VkExtensionProperties> availableExtensions(extensionCount);
    vkEnumerateDeviceExtensionProperties(this->_physicalDevice, nullptr, &extensionCount, availableExtensions.data());
    for (const auto& extension : availableExtensions) {
        if (strcmp(extension.extensionName, "VK_KHR_portability_subset") == 0) {
            localDeviceExtensions.push_back("VK_KHR_portability_subset");
            break;
        }
    }
    createInfo.enabledExtensionCount = static_cast<uint32_t>(localDeviceExtensions.size());
    createInfo.ppEnabledExtensionNames = localDeviceExtensions.data();

    if (enableValidationLayers)
    {
        createInfo.enabledLayerCount = static_cast<uint32_t>(ValidationLayers.size());
        createInfo.ppEnabledLayerNames = ValidationLayers.data();
    }
    else
    {
        createInfo.enabledLayerCount = 0;
    }

    if (vkCreateDevice(this->_physicalDevice, &createInfo, nullptr, &this->_vkdevice) != VK_SUCCESS)
    {
        throw std::runtime_error("Failed to create logical device!");
    }

    std::cout << "create logical device success" << std::endl;
    vkGetDeviceQueue(this->_vkdevice, indices.graphicsFamily, 0, &this->_graphicsQueue);
    vkGetDeviceQueue(this->_vkdevice, indices.presentFamily, 0, &this->_presentQueue);
}
VkQueue CE_VulkanManager::getPresentQueue()
{
    return this->_presentQueue;
}
VkQueue CE_VulkanManager::getGraphicsQueue()
{
    return this->_graphicsQueue;
}
void CE_VulkanManager ::drawFrame()
{
    this->_context->drawFrame();
}
/**
 * @brief 添加UI对象
 *
 * @param id
 */
void CE_VulkanManager::addUIObject(std::string id)
{
    this->_context->addUIObject(id);
}
void CE_VulkanManager::updateUIObjectShader(std::string id, std::string shaderPath)
{
    this->_context->updateUIObjectShader(id, shaderPath);
}
void CE_VulkanManager::updateUIObjectVertexs(std::string id, std::vector<float> positions, std::vector<int> indices)
{
    this->_context->updateUIObjectVertexs(id, positions, indices);
}
void CE_VulkanManager::updateUIObjectColor(std::string id, float r, float g, float b, float a)
{
    this->_context->updateUIObjectColor(id, r, g, b, a);
}

// 读取shader内容
std::vector<char> CE_VulkanManager::readShaderFile(const std::string &filename)
{
    // ate: 表示从文件末未开始读取
    // binary: 表示以二进制方式读取
    std::ifstream file(filename, std::ios::ate | std::ios::binary);
    std::cout << filename << std::endl;
    if (!file.is_open())
    {
        throw std::runtime_error("failed to open file!");
    }
    // tellg()返回当前定位指针的位置，也代表着输入流的大小。
    size_t fileSize = (size_t)file.tellg();
    std::cout << fileSize << std::endl;
    std::vector<char> buffer(fileSize);
    // seekg()是对输入流的操作 g是get缩写，0是代表从开头读起。
    file.seekg(0);
    // 读入到Buffer当中
    file.read(buffer.data(), fileSize);
    file.close();
    std::cout << "read file success: " << filename << std::endl;
    return buffer;
}
// 要将着色器字节码在管线上使用，还需要使用VkShaderModule转换
VkShaderModule CE_VulkanManager::createShaderModule(const std::vector<char> &code)
{
    VkShaderModuleCreateInfo createInfo{};
    createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
    createInfo.codeSize = code.size();
    createInfo.pCode = reinterpret_cast<const uint32_t *>(code.data());

    std::cout << "shader code size: " << code.size() << "\n";

    VkShaderModule shaderModule;
    if (vkCreateShaderModule(this->_vkdevice, &createInfo, nullptr, &shaderModule) != VK_SUCCESS)
    {
        throw std::runtime_error("Failed to create shader module!");
    }

    return shaderModule;
}

VkResult CE_VulkanManager::createBuffer(VkBufferUsageFlags usageFlags, VkMemoryPropertyFlags memoryPropertyFlags, VkBuffer *buffer, VkDeviceMemory *memory, VkDeviceSize size, void *data = nullptr)
{
    VkBufferCreateInfo bufferCreateInfo{};
    bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
    bufferCreateInfo.usage = usageFlags;
    bufferCreateInfo.size = size;
    bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
    vkCreateBuffer(this->_vkdevice, &bufferCreateInfo, nullptr, buffer);

    VkMemoryRequirements memReqs;
    VkMemoryAllocateInfo memAlloc{};
    memAlloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
    vkGetBufferMemoryRequirements(this->_vkdevice, *buffer, &memReqs);
    memAlloc.allocationSize = memReqs.size;
    memAlloc.memoryTypeIndex = getMemoryTypeIndex(memReqs.memoryTypeBits, memoryPropertyFlags);
    vkAllocateMemory(this->_vkdevice, &memAlloc, nullptr, memory);

    if (data != nullptr)
    {
        void *mapped;
        vkMapMemory(this->_vkdevice, *memory, 0, size, 0, &mapped);
        memcpy(mapped, data, size);
        vkUnmapMemory(this->_vkdevice, *memory);
    }

    vkBindBufferMemory(this->_vkdevice, *buffer, *memory, 0);
    return VK_SUCCESS;
}
uint32_t CE_VulkanManager::getMemoryTypeIndex(uint32_t typeBits, VkMemoryPropertyFlags properties)
{
    VkPhysicalDeviceMemoryProperties deviceMemoryProperties;
    vkGetPhysicalDeviceMemoryProperties(this->_physicalDevice, &deviceMemoryProperties);
    for (uint32_t i = 0; i < deviceMemoryProperties.memoryTypeCount; i++)
    {
        if ((typeBits & 1) == 1)
        {
            if ((deviceMemoryProperties.memoryTypes[i].propertyFlags & properties) == properties)
            {
                return i;
            }
        }
        typeBits >>= 1;
    }
    return 0;
}

CE_VulkanManager::~CE_VulkanManager()
{
}
